
#include "BfcpNetTuple.hxx"
#include "rutil/compat.hxx"

#include <iostream>
#include <string.h>
#include <sys/types.h>
#include "rutil/ResipAssert.h"

#if !defined (WIN32)
#include <arpa/inet.h>
#include <netinet/in.h>
#if defined(__APPLE__) && !defined(s6_addr16)
#define s6_addr16 __u6_addr.__u6_addr16
#endif
#endif

#include "rutil/Data.hxx"
#include "rutil/DnsUtil.hxx"
#include "rutil/GenericIPAddress.hxx"
#include "rutil/HashMap.hxx"
#include "rutil/MD5Stream.hxx"
#ifdef USE_NETNS
#   include "rutil/NetNs.hxx"
#endif
#include "zklog/zklog_wrapper.h"


namespace bfcp {
    using namespace resip;

    BfcpNetTuple::BfcpNetTuple() :
        mFlowKey(0),
        mTransportKey(0),
        onlyUseExistingConnection(false),
        mTransportType(UNKNOWN_TRANSPORT)
    {
        sockaddr_in* addr4 = (sockaddr_in*)&mSockaddr;
        memset(addr4, 0, sizeof(sockaddr_in));
        mSockaddr.sa_family = AF_INET;
    }

    BfcpNetTuple::BfcpNetTuple(const GenericIPAddress& genericAddress, TransportType type,
        const Data& targetDomain) :
        mFlowKey(0),
        mTransportKey(0),
        onlyUseExistingConnection(false),
        mTransportType(type),
        mTargetDomain(targetDomain)
    {
        setSockaddr(genericAddress);
    }


    BfcpNetTuple::BfcpNetTuple(const Data& printableAddr,
        int port,
        IpVersion ipVer,
        TransportType type,
        const Data& targetDomain,
        const Data& netNs) :
        mFlowKey(0),
        mTransportKey(0),
        onlyUseExistingConnection(false),
        mTransportType(type),
        mTargetDomain(targetDomain),
        mNetNs(netNs)
    {
        if (ipVer == V4)
        {
            memset(&m_anonv4, 0, sizeof(m_anonv4));
            m_anonv4.sin_family = AF_INET;
            m_anonv4.sin_port = htons(port);

            if (printableAddr.empty())
            {
                m_anonv4.sin_addr.s_addr = htonl(INADDR_ANY);
            }
            else
            {
                DnsUtil::inet_pton(printableAddr, m_anonv4.sin_addr);
            }
        }
        else
        {
#ifdef USE_IPV6
            memset(&m_anonv6, 0, sizeof(m_anonv6));
            m_anonv6.sin6_family = AF_INET6;
            m_anonv6.sin6_port = htons(port);
            if (printableAddr.empty())
            {
                m_anonv6.sin6_addr = in6addr_any;
            }
            else
            {
                DnsUtil::inet_pton(printableAddr, m_anonv6.sin6_addr);
            }
#else
            // avoid asserts since BfcpNetTuples created via printableAddr can be created from items 
            // received from the wire or from configuration settings.  Just create an IPV4 inaddr_any BfcpNetTuple.
            //assert(0);  
            memset(&m_anonv4, 0, sizeof(m_anonv4));
            m_anonv4.sin_family = AF_INET;
            m_anonv4.sin_port = htons(port);
            m_anonv4.sin_addr.s_addr = htonl(INADDR_ANY);
#endif
        }
    }

    BfcpNetTuple::BfcpNetTuple(const Data& printableAddr,
        int port,
        TransportType ptype,
        const Data& targetDomain,
        const Data& netNs) :
        mFlowKey(0),
        mTransportKey(0),
        onlyUseExistingConnection(false),
        mTransportType(ptype),
        mTargetDomain(targetDomain),
        mNetNs(netNs)
    {
        if (DnsUtil::isIpV4Address(printableAddr))
        {
            memset(&m_anonv4, 0, sizeof(m_anonv4));

            DnsUtil::inet_pton(printableAddr, m_anonv4.sin_addr);
            m_anonv4.sin_family = AF_INET;
            m_anonv4.sin_port = htons(port);
        }
#ifdef USE_IPV6
        else if (DnsUtil::isIpV6Address(printableAddr))
        {
            memset(&m_anonv6, 0, sizeof(m_anonv6));
            DnsUtil::inet_pton(printableAddr, m_anonv6.sin6_addr);
            m_anonv6.sin6_family = AF_INET6;
            m_anonv6.sin6_port = htons(port);
        }
#endif
        else
        {
            // avoid asserts since BfcpNetTuples created via printableAddr can be created from items 
            // received from the wire or from configuration settings.  Just create an IPV4 inaddr_any BfcpNetTuple.
            // assert(0);  
            memset(&m_anonv4, 0, sizeof(m_anonv4));
            m_anonv4.sin_family = AF_INET;
            m_anonv4.sin_port = htons(port);
            m_anonv4.sin_addr.s_addr = htonl(INADDR_ANY);
        }
    }

    BfcpNetTuple::BfcpNetTuple(const in_addr& ipv4,
        int port,
        TransportType ptype,
        const Data& targetDomain,
        const Data& netNs)
        :mFlowKey(0),
        mTransportKey(0),
        onlyUseExistingConnection(false),
        mTransportType(ptype),
        mTargetDomain(targetDomain),
        mNetNs(netNs)
    {
        memset(&m_anonv4, 0, sizeof(sockaddr_in));
        m_anonv4.sin_addr = ipv4;
        m_anonv4.sin_port = htons(port);
        m_anonv4.sin_family = AF_INET;
    }

#ifdef USE_IPV6
    BfcpNetTuple::BfcpNetTuple(const in6_addr& ipv6,
        int port,
        TransportType ptype,
        const Data& targetDomaina,
        const Data& netNs)
        :mFlowKey(0),
        mTransportKey(0),
        onlyUseExistingConnection(false),
        mTransportType(ptype),
        mTargetDomain(targetDomaina),
        mNetNs(netNs)
    {
        memset(&m_anonv6, 0, sizeof(sockaddr_in6));
        m_anonv6.sin6_addr = ipv6;
        m_anonv6.sin6_port = htons(port);
        m_anonv6.sin6_family = AF_INET6;
    }
#endif

    BfcpNetTuple::BfcpNetTuple(const struct sockaddr& addr,
        TransportType ptype,
        const Data& targetDomain) :
        mFlowKey(0),
        mTransportKey(0),
        onlyUseExistingConnection(false),
        mSockaddr(addr),
        mTransportType(ptype),
        mTargetDomain(targetDomain)
    {
        if (addr.sa_family == AF_INET)
        {
            m_anonv4 = (sockaddr_in&)(addr);
        }
#ifdef USE_IPV6
        else if (addr.sa_family == AF_INET6)
        {
            m_anonv6 = (sockaddr_in6&)(addr);
        }
#endif
        else
        {
            resip_assert(0);
        }
    }

    void
        BfcpNetTuple::copySockaddrAnyPort(sockaddr *sa)
    {
        memcpy(sa, &mSockaddr, length());
        // zero the port number
        if (sa->sa_family == AF_INET)
        {
            ((sockaddr_in*)sa)->sin_port = 0;
        }
#ifdef USE_IPV6
        else if (sa->sa_family == AF_INET6)
        {
            ((sockaddr_in6*)sa)->sin6_port = 0;
        }
#endif
        else
        {
            resip_assert(0);
        }
    }

    void
        BfcpNetTuple::setSockaddr(const GenericIPAddress& addr)
    {
        if (addr.isVersion4())
        {
            m_anonv4 = addr.v4Address;
        }
        else
#ifdef USE_IPV6
        {
            m_anonv6 = addr.v6Address;
        }
#else
        {
            resip_assert(0);
        }
#endif
    }

#ifdef USE_NETNS
#   define TOKEN_SIZE 8
#   define TOKEN_IP_ADDRESS_OFFSET 4
#else
#   define TOKEN_SIZE 7
#   define TOKEN_IP_ADDRESS_OFFSET 3
#endif

    void
        BfcpNetTuple::writeBinaryToken(const BfcpNetTuple& BfcpNetTuple, resip::Data& container, const Data& salt)
    {
        // .bwc. Maybe should just write the raw sockaddr into a buffer, and tack
        // on the flowid and onlyUseExistingConnection flag. Would require 10 extra
        // bytes for V6, and 14 extra bytes for V4. 
        // V6: sin6_len(1), sin6_flowinfo(4), flowId(4), onlyUseExistingConnection(1)
        // V4: sin_family(2 instead of 1), sin_zero(8), flowId(4), onlyUseExistingConnection(1)
        UInt32 rawToken[TOKEN_SIZE];
        memset(&rawToken, 0, TOKEN_SIZE * 4);

        rawToken[0] = BfcpNetTuple.mFlowKey;

        rawToken[1] = BfcpNetTuple.mTransportKey;

        // 0xXXXX0000
        rawToken[2] += (BfcpNetTuple.getPort() << 16);

        // 0x0000XX00
        rawToken[2] += (BfcpNetTuple.getType() << 8);

        // 0x000000X0
        if (BfcpNetTuple.onlyUseExistingConnection)
        {
            rawToken[2] += 0x00000010;
        }

#ifdef USE_NETNS
        rawToken[3] = NetNs::getNetNsId(BfcpNetTuple.getNetNs());
#endif

#ifdef USE_IPV6
        if (BfcpNetTuple.ipVersion() == V6)
        {
            // 0x0000000X
            rawToken[2] += 0x00000001;
            in6_addr address = reinterpret_cast<const sockaddr_in6&>(BfcpNetTuple.getSockaddr()).sin6_addr;
            resip_assert(sizeof(address) == 16);
            memcpy(&rawToken[TOKEN_IP_ADDRESS_OFFSET], &address, 16);
        }
        else
#endif
        {
            in_addr address = reinterpret_cast<const sockaddr_in&>(BfcpNetTuple.getSockaddr()).sin_addr;
            resip_assert(sizeof(address) == 4);
            memcpy(&rawToken[TOKEN_IP_ADDRESS_OFFSET], &address, 4);
        }

        container.clear();
        container.reserve(((BfcpNetTuple.ipVersion() == V6) ? TOKEN_SIZE * 4 : (TOKEN_SIZE - 3) * 4) + (salt.empty() ? 0 : 32));
        container.append((char*)&rawToken[0], (BfcpNetTuple.ipVersion() == V6) ? TOKEN_SIZE * 4 : (TOKEN_SIZE - 3) * 4);

        if (!salt.empty())
        {
            // TODO - potentially use SHA1 HMAC if USE_SSL is defined for stronger encryption
            MD5Stream ms;
            ms << container << salt;
            container += ms.getHex();
        }
    }


    BfcpNetTuple
        BfcpNetTuple::makeTupleFromBinaryToken(const resip::Data& binaryFlowToken, const Data& salt)
    {
        // To check if size is valid, we first need the IP version, so make sure the token is at least
        // the size of an IPv4 token
        if (binaryFlowToken.size() < 16)
        {
            // !bwc! Should not assert here, since this sort of thing
            // can come off the wire easily.
            // TODO Throw an exception here?
            ZKLOG_DEBUG << "binary flow token was too small: " << binaryFlowToken.size();
            return BfcpNetTuple();
        }

        const UInt32* rawToken = reinterpret_cast<const UInt32*>(binaryFlowToken.data());

        FlowKey mFlowKey = rawToken[0];
        TransportKey transportKey = rawToken[1];

        IpVersion version = (rawToken[2] & 0x00000001 ? V6 : V4);

        bool isRealFlow = (rawToken[2] & 0x00000010 ? true : false);

        UInt8 temp = (TransportType)((rawToken[2] & 0x00000F00) >> 8);
        if (temp >= MAX_TRANSPORT)
        {
            ZKLOG_DEBUG << "Garbage transport type in flow token: " << temp;
            return BfcpNetTuple();
        }
        TransportType type = (TransportType)temp;

        UInt16 port = (rawToken[2] >> 16);

        // Now that we have the version we can do a more accurate check on the size
        if (!((version == V4 && salt.empty() && binaryFlowToken.size() == (TOKEN_SIZE - 3) * 4) ||
            (version == V4 && !salt.empty() && binaryFlowToken.size() == (TOKEN_SIZE - 3) * 4 + 32) ||
            (version == V6 && salt.empty() && binaryFlowToken.size() == TOKEN_SIZE * 4) ||
            (version == V6 && !salt.empty() && binaryFlowToken.size() == TOKEN_SIZE * 4 + 32)))
        {
            ZKLOG_DEBUG << "Binary flow token is the wrong size for its IP version.";
            return BfcpNetTuple();
        }

        // If salt is specified, validate HMAC
        if (!salt.empty())
        {
            unsigned int tokenSizeLessHMAC = version == V4 ? (TOKEN_SIZE - 3) * 4 : TOKEN_SIZE * 4;
            Data flowTokenLessHMAC(Data::Share, binaryFlowToken.data(), tokenSizeLessHMAC);
            Data flowTokenHMAC(Data::Share, binaryFlowToken.data() + tokenSizeLessHMAC, 32);
            MD5Stream ms;
            ms << flowTokenLessHMAC << salt;
            if (ms.getHex() != flowTokenHMAC)
            {
                ZKLOG_DEBUG << "Binary flow token has invalid HMAC, not our token";
                return BfcpNetTuple();
            }
        }

        Data netNs("");
#ifdef USE_NETNS
        int netNsId = rawToken[3];
        try
        {
            netNs = NetNs::getNetNsName(netNsId);
        }
        catch (NetNs::Exception e)
        {
            ErrLog(<< "BfcpNetTuple binary token contained netns id: " << netNsId << "which does not exist."
                << e);
        }
#endif

        if (version == V6)
        {
#ifdef USE_IPV6
            in6_addr address;
            resip_assert(sizeof(address) == 16);
            memcpy(&address, &rawToken[TOKEN_IP_ADDRESS_OFFSET], 16);
            BfcpNetTuple result(address, port, type, Data::Empty, netNs);
#else
            BfcpNetTuple result(resip::Data::Empty, port, type, Data::Empty, netNs);
#endif
            result.mFlowKey = (FlowKey)mFlowKey;
            result.mTransportKey = (TransportKey)transportKey;
            result.onlyUseExistingConnection = isRealFlow;
            return result;
        }

        in_addr address;
        resip_assert(sizeof(address) == 4);
        memcpy(&address, &rawToken[TOKEN_IP_ADDRESS_OFFSET], 4);
        BfcpNetTuple result(address, port, type, Data::Empty, netNs);
        result.mFlowKey = (FlowKey)mFlowKey;
        result.mTransportKey = (TransportKey)transportKey;
        result.onlyUseExistingConnection = isRealFlow;
        return result;
    }

    Data
        BfcpNetTuple::presentationFormat() const
    {
#ifdef USE_IPV6
        if (isV4())
        {
            return BfcpNetTuple::inet_ntop(*this);
        }
        else if (IN6_IS_ADDR_V4MAPPED(&m_anonv6.sin6_addr))
        {
            return DnsUtil::inet_ntop(*(reinterpret_cast<const in_addr*>(
                (reinterpret_cast<const unsigned char*>(&m_anonv6.sin6_addr) + 12))));
        }
        else
        {
            return BfcpNetTuple::inet_ntop(*this);
        }
#else
        return BfcpNetTuple::inet_ntop(*this);
#endif

    }

    void
        BfcpNetTuple::setPort(int port)
    {
        if (mSockaddr.sa_family == AF_INET) // v4   
        {
            m_anonv4.sin_port = htons(port);
        }
        else
        {
#ifdef USE_IPV6
            m_anonv6.sin6_port = htons(port);
#else
            resip_assert(0);
#endif
        }
    }

    int
        BfcpNetTuple::getPort() const
    {
        if (mSockaddr.sa_family == AF_INET) // v4   
        {
            return ntohs(m_anonv4.sin_port);
        }
        else
        {
#ifdef USE_IPV6
            return ntohs(m_anonv6.sin6_port);
#else
            resip_assert(0);
#endif
        }

        return -1;
    }

    bool
        BfcpNetTuple::isAnyInterface() const
    {
        if (isV4())
        {
            return m_anonv4.sin_addr.s_addr == htonl(INADDR_ANY);
        }
#if defined (USE_IPV6)
        else
        {
            return memcmp(&m_anonv6.sin6_addr, &in6addr_any, sizeof(in6_addr)) == 0;
        }
#else
        return false;
#endif
    }

    static BfcpNetTuple loopbackv4("127.0.0.1", 0, UNKNOWN_TRANSPORT);
    bool
        BfcpNetTuple::isLoopback() const
    {
        if (ipVersion() == V4)
        {
            return isEqualWithMask(loopbackv4, 8, true, true);
        }
        else if (ipVersion() == V6)
        {
#ifdef USE_IPV6
#if defined(__linux__) || defined(__APPLE__) || defined(WIN32)
            return IN6_IS_ADDR_LOOPBACK(&(m_anonv6.sin6_addr)) != 0;
#else
            return ((*(const __uint32_t *)(const void *)(&(m_anonv6.sin6_addr.s6_addr[0])) == 0) &&
                (*(const __uint32_t *)(const void *)(&(m_anonv6.sin6_addr.s6_addr[4])) == 0) &&
                (*(const __uint32_t *)(const void *)(&(m_anonv6.sin6_addr.s6_addr[8])) == 0) &&
                (*(const __uint32_t *)(const void *)(&(m_anonv6.sin6_addr.s6_addr[12])) == ntohl(1)));
#endif
#endif
        }
        else
        {
            resip_assert(0);
        }

        return false;
    }

    bool
        BfcpNetTuple::isV4() const
    {
        return mSockaddr.sa_family == AF_INET;
    }

    IpVersion
        BfcpNetTuple::ipVersion() const
    {
        return mSockaddr.sa_family == AF_INET ? V4 : V6;
    }

    static BfcpNetTuple v4privateaddrbase1("10.0.0.0", 0, UNKNOWN_TRANSPORT);
    static BfcpNetTuple v4privateaddrbase2("172.16.0.0", 0, UNKNOWN_TRANSPORT);
    static BfcpNetTuple v4privateaddrbase3("192.168.0.0", 0, UNKNOWN_TRANSPORT);

#ifdef USE_IPV6
    static BfcpNetTuple v6privateaddrbase("fc00::", 0, UNKNOWN_TRANSPORT);
#endif

    bool
        BfcpNetTuple::isPrivateAddress() const
    {
        if (ipVersion() == V4)
        {
            // RFC 1918
            return isEqualWithMask(v4privateaddrbase1, 8, true, true) ||  // 10.0.0.0        -   10.255.255.255  (10/8 prefix)
                isEqualWithMask(v4privateaddrbase2, 12, true, true) || // 172.16.0.0      -   172.31.255.255  (172.16/12 prefix)
                isEqualWithMask(v4privateaddrbase3, 16, true, true) || // 192.168.0.0     -   192.168.255.255 (192.168/16 prefix)
                isLoopback();
        }
#ifdef USE_IPV6
        else if (ipVersion() == V6)
        {
            // RFC 4193
            // ?slg? should we look specifically for ipv4 mapped/compatible address and apply V4 rules to them?
            return isEqualWithMask(v6privateaddrbase, 7, true, true) ||  // fc00::/7
                isLoopback();
        }
#endif
        else
        {
            resip_assert(0);
        }

        return false;
    }

    socklen_t
        BfcpNetTuple::length() const
    {
        if (mSockaddr.sa_family == AF_INET) // v4
        {
            return sizeof(sockaddr_in);
        }
#ifdef USE_IPV6
        else  if (mSockaddr.sa_family == AF_INET6) // v6
        {
            return sizeof(sockaddr_in6);
        }
#endif

        resip_assert(0);
        return 0;
    }


    bool BfcpNetTuple::operator==(const BfcpNetTuple& rhs) const
    {
        if (mSockaddr.sa_family == rhs.mSockaddr.sa_family)
        {
            if (mSockaddr.sa_family == AF_INET) // v4
            {
                return (m_anonv4.sin_port == rhs.m_anonv4.sin_port &&
                    mTransportType == rhs.mTransportType &&
                    memcmp(&m_anonv4.sin_addr, &rhs.m_anonv4.sin_addr, sizeof(in_addr)) == 0 &&
                    rhs.mNetNs == mNetNs);
            }
            else // v6
            {
#ifdef USE_IPV6
                return (m_anonv6.sin6_port == rhs.m_anonv6.sin6_port &&
                    mTransportType == rhs.mTransportType &&
                    memcmp(&m_anonv6.sin6_addr, &rhs.m_anonv6.sin6_addr, sizeof(in6_addr)) == 0 &&
                    rhs.mNetNs == mNetNs);
#else
                resip_assert(0);
                return false;
#endif
            }
        }
        else
        {
            return false;
        }

        // !dlb! don't include connection 
    }

    bool
        BfcpNetTuple::operator<(const BfcpNetTuple& rhs) const
    {
        if (mTransportType < rhs.mTransportType)
        {
            return true;
        }
        else if (mTransportType > rhs.mTransportType)
        {
            return false;
        }

#ifdef USE_NETNS
        // netns needs to be checked before port and address as the port/address 
        // comparison bails out in equal case.  Ideally netns comparison should
        // be last as its the most expensive comparison.  For now putting it here
        // for minimal code change
        else if (mNetNs < rhs.mNetNs)
        {
            return(true);
        }
        else if (mNetNs > rhs.mNetNs)
        {
            return(false);
        }
#endif

        else if (mSockaddr.sa_family == AF_INET && rhs.mSockaddr.sa_family == AF_INET)
        {
            int c = memcmp(&m_anonv4.sin_addr,
                &rhs.m_anonv4.sin_addr,
                sizeof(in_addr));

            if (c < 0)
            {
                return true;
            }
            else if (c > 0)
            {
                return false;
            }
            else if (m_anonv4.sin_port < rhs.m_anonv4.sin_port)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
#ifdef USE_IPV6
        else if (mSockaddr.sa_family == AF_INET6 &&
            rhs.mSockaddr.sa_family == AF_INET6)
        {
            int c = memcmp(&m_anonv6.sin6_addr,
                &rhs.m_anonv6.sin6_addr,
                sizeof(in6_addr));
            if (c < 0)
            {
                return true;
            }
            else if (c > 0)
            {
                return false;
            }
            else if (m_anonv6.sin6_port < rhs.m_anonv6.sin6_port)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else if (mSockaddr.sa_family == AF_INET6 &&
            rhs.mSockaddr.sa_family == AF_INET)
        {
            return true;
        }
        else if (mSockaddr.sa_family == AF_INET &&
            rhs.mSockaddr.sa_family == AF_INET6)
        {
            return false;
        }
#endif

        else
        {
            //assert(0);
            return false;
        }
    }

    EncodeStream& operator<<(EncodeStream& ostrm, const BfcpNetTuple& BfcpNetTuple)
    {
        ostrm << "[ ";

#ifdef USE_IPV6
        if (BfcpNetTuple.mSockaddr.sa_family == AF_INET6)
        {
            ostrm << "V6 " << DnsUtil::inet_ntop(BfcpNetTuple.m_anonv6.sin6_addr) << " port=" << BfcpNetTuple.getPort();
        }
        else
#endif
            if (BfcpNetTuple.mSockaddr.sa_family == AF_INET)
            {
                ostrm << "V4 " << BfcpNetTuple::inet_ntop(BfcpNetTuple) << ":" << BfcpNetTuple.getPort();
            }
            else
            {
                resip_assert(0);
            }

        ostrm << " " << BfcpNetTuple::toData(BfcpNetTuple.mTransportType);

        if (!BfcpNetTuple.mTargetDomain.empty())
        {
            ostrm << " targetDomain=" << BfcpNetTuple.mTargetDomain;
        }

        if (BfcpNetTuple.mFlowKey != 0)
        {
            ostrm << " flowKey=" << BfcpNetTuple.mFlowKey;
        }

        if (BfcpNetTuple.mTransportKey != 0)
        {
            ostrm << " transportKey=" << BfcpNetTuple.mTransportKey;
        }

#ifdef USE_NETNS
        ostrm << " mNetNs=" << BfcpNetTuple.mNetNs;
#endif

        ostrm << " ]";

        return ostrm;
    }

    size_t
        BfcpNetTuple::hash() const
    {
        // !dlb! do not include the connection
#ifdef USE_IPV6
        if (mSockaddr.sa_family == AF_INET6)
        {
            const sockaddr_in6& in6 =
                reinterpret_cast<const sockaddr_in6&>(mSockaddr);

            return size_t(Data(Data::Share, (const char *)&in6.sin6_addr.s6_addr, sizeof(in6.sin6_addr.s6_addr)).hash() +
#ifdef USE_NETNS
                mNetNs.hash() +
#endif
                5 * in6.sin6_port +
                25 * mTransportType);
        }
        else
#endif
        {
            const sockaddr_in& in4 =
                reinterpret_cast<const sockaddr_in&>(mSockaddr);

            return size_t(in4.sin_addr.s_addr +
#ifdef USE_NETNS
                mNetNs.hash() +
#endif
                5 * in4.sin_port +
                25 * mTransportType);
        }
    }

    TransportType
        BfcpNetTuple::toTransport(const Data& transportName)
    {
        return resip::toTransportType(transportName); // TransportTypes.hxx
    };

    const Data&
        BfcpNetTuple::toData(TransportType type)
    {
        return resip::toData(type);  // TransportTypes.hxx
    }

    const Data&
        BfcpNetTuple::toDataLower(TransportType type)
    {
        return resip::toDataLower(type);  // TransportTypes.hxx
    }

    Data
        BfcpNetTuple::inet_ntop(const BfcpNetTuple& BfcpNetTuple)
    {
#ifdef USE_IPV6
        if (!BfcpNetTuple.isV4())
        {
            const sockaddr_in6& addr = reinterpret_cast<const sockaddr_in6&>(BfcpNetTuple.getSockaddr());
            return DnsUtil::inet_ntop(addr.sin6_addr);
        }
        else
#endif
        {
            const sockaddr_in& addr = reinterpret_cast<const sockaddr_in&>(BfcpNetTuple.getSockaddr());
            return DnsUtil::inet_ntop(addr.sin_addr);
        }
    }


    bool
        BfcpNetTuple::isEqualWithMask(const BfcpNetTuple& compare, short mask, bool ignorePort, bool ignoreTransport) const
    {
        if (ignoreTransport || getType() == compare.getType())  // check if transport type matches
        {
            if (mSockaddr.sa_family == compare.getSockaddr().sa_family && mSockaddr.sa_family == AF_INET) // v4
            {
                sockaddr_in* addr1 = (sockaddr_in*)&mSockaddr;
                sockaddr_in* addr2 = (sockaddr_in*)&compare.getSockaddr();

                return ((ignorePort || addr1->sin_port == addr2->sin_port) &&
                    (addr1->sin_addr.s_addr & htonl((0xFFFFFFFF << (32 - mask)))) ==
                    (addr2->sin_addr.s_addr & htonl((0xFFFFFFFF << (32 - mask)))));
            }
#ifdef USE_IPV6
            else if (mSockaddr.sa_family == compare.getSockaddr().sa_family && mSockaddr.sa_family == AF_INET6) // v6
            {
                sockaddr_in6* addr1 = (sockaddr_in6*)&mSockaddr;
                sockaddr_in6* addr2 = (sockaddr_in6*)&compare.getSockaddr();

                if (ignorePort || addr1->sin6_port == addr2->sin6_port)
                {
                    UInt32 mask6part;
                    UInt32 temp;
                    bool match = true;
                    for (int i = 3; i >= 0; i--)
                    {
                        if (mask <= 32 * i)
                        {
                            mask6part = 0;
                        }
                        else
                        {
                            temp = mask - 32 * i;
                            if (temp >= 32)
                            {
                                mask6part = 0xffffffff;
                            }
                            else
                            {
                                mask6part = 0xffffffff << (32 - temp);
                            }
                        }
#ifdef WIN32
                        if ((*((unsigned long*)&addr1->sin6_addr.u.Word[i * 2]) & htonl(mask6part)) !=
                            (*((unsigned long*)&addr2->sin6_addr.u.Word[i * 2]) & htonl(mask6part)))
#elif defined(sun)
                        // sun has no s6_addr16
                        if ((*((unsigned long*)&addr1->sin6_addr._S6_un._S6_u32[i]) & htonl(mask6part)) !=
                            (*((unsigned long*)&addr2->sin6_addr._S6_un._S6_u32[i]) & htonl(mask6part)))
#elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__)
                        // bsd has no s6_addr16
                        if ((*((unsigned long*)&addr1->sin6_addr.__u6_addr.__u6_addr32[i]) & htonl(mask6part)) !=
                            (*((unsigned long*)&addr2->sin6_addr.__u6_addr.__u6_addr32[i]) & htonl(mask6part)))
#else
                        if ((*((UInt32*)&addr1->sin6_addr.s6_addr16[i * 2]) & htonl(mask6part)) !=
                            (*((UInt32*)&addr2->sin6_addr.s6_addr16[i * 2]) & htonl(mask6part)))
#endif
                        {
                            match = false;
                            break;
                        }
                    }
                    if (match)
                    {
                        return true;
                    }
                }
            }
#endif
        }
        return false;
    }


    // special comparitors
    bool
        BfcpNetTuple::AnyInterfaceCompare::operator()(const BfcpNetTuple& lhs,
            const BfcpNetTuple& rhs) const
    {
        if (lhs.mTransportType < rhs.mTransportType)
        {
            return true;
        }
        else if (lhs.mTransportType > rhs.mTransportType)
        {
            return false;
        }
        else if (lhs.mSockaddr.sa_family == AF_INET && rhs.mSockaddr.sa_family == AF_INET)
        {
            if (lhs.m_anonv4.sin_port < rhs.m_anonv4.sin_port)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
#ifdef USE_IPV6
        else if (lhs.mSockaddr.sa_family == AF_INET6 &&
            rhs.mSockaddr.sa_family == AF_INET6)
        {
            if (lhs.m_anonv6.sin6_port < rhs.m_anonv6.sin6_port)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else if (lhs.mSockaddr.sa_family == AF_INET6 &&
            rhs.mSockaddr.sa_family == AF_INET)
        {
            return true;
        }
        else if (lhs.mSockaddr.sa_family == AF_INET &&
            rhs.mSockaddr.sa_family == AF_INET6)
        {
            return false;
        }
#endif
        else
        {
            return false;
        }
    };

    bool
        BfcpNetTuple::AnyPortCompare::operator()(const BfcpNetTuple& lhs,
            const BfcpNetTuple& rhs) const
    {
        if (lhs.mTransportType < rhs.mTransportType)
        {
            return true;
        }
        else if (lhs.mTransportType > rhs.mTransportType)
        {
            return false;
        }

        // transport types equal, so compare addresses
        if (lhs.mSockaddr.sa_family == AF_INET && rhs.mSockaddr.sa_family == AF_INET)
        {
            int c = memcmp(&lhs.m_anonv4.sin_addr,
                &rhs.m_anonv4.sin_addr,
                sizeof(in_addr));

            if (c < 0)
            {
                return true;
            }
            else if (c > 0)
            {
                return false;
            }
        }
#ifdef USE_IPV6
        else if (lhs.mSockaddr.sa_family == AF_INET6 &&
            rhs.mSockaddr.sa_family == AF_INET6)
        {
            int c = memcmp(&lhs.m_anonv6.sin6_addr,
                &rhs.m_anonv6.sin6_addr,
                sizeof(in6_addr));
            if (c < 0)
            {
                return true;
            }
            else if (c > 0)
            {
                return false;
            }
        }
        else if (lhs.mSockaddr.sa_family == AF_INET6 &&
            rhs.mSockaddr.sa_family == AF_INET)
        {
            return true;
        }
        else if (lhs.mSockaddr.sa_family == AF_INET &&
            rhs.mSockaddr.sa_family == AF_INET6)
        {
            return false;
        }
#endif
#ifdef USE_NETNS
        // transport type and addresses are equal, so compare netns
        if (lhs.mNetNs < rhs.mNetNs)
        {
            //DebugLog(<< "AnyPortCompare netns less than (l=" << lhs.mNetNs << ", r=" << rhs.mNetNs);
            return(true);
        }
        else if (rhs.mNetNs < lhs.mNetNs)
        {
            //DebugLog(<< "AnyPortCompare netns greater than (l=" << lhs.mNetNs << ", r=" << rhs.mNetNs);
            return(false);
        }
        //DebugLog(<< "AnyPortCompare netns equal to (l=\"" << lhs.mNetNs << "\", r=\"" << rhs.mNetNs << "\"");
#endif

        return false;
    }

    bool
        BfcpNetTuple::FlowKeyCompare::operator()(const BfcpNetTuple& lhs,
            const BfcpNetTuple& rhs) const
    {
        if (lhs == rhs)
        {
            return lhs.mFlowKey < rhs.mFlowKey;
        }
        return lhs < rhs;
    };

    GenericIPAddress
        BfcpNetTuple::toGenericIPAddress() const
    {
        if (isV4())
        {
            return GenericIPAddress(m_anonv4);
        }
        else
#ifdef USE_IPV6
        {
            return GenericIPAddress(m_anonv6);
        }
#else
        {
            resip_assert(0);
            return m_anonv4; //bogus
        }
#endif
    }

    bool
        BfcpNetTuple::AnyPortAnyInterfaceCompare::operator()(const BfcpNetTuple& lhs,
            const BfcpNetTuple& rhs) const
    {
        if (lhs.mTransportType < rhs.mTransportType)
        {
            return true;
        }
        else if (lhs.mTransportType > rhs.mTransportType)
        {
            return false;
        }
#ifdef USE_IPV6
        else if (lhs.mSockaddr.sa_family == AF_INET6 &&
            rhs.mSockaddr.sa_family == AF_INET)
        {
            return true;
        }
        else if (lhs.mSockaddr.sa_family == AF_INET &&
            rhs.mSockaddr.sa_family == AF_INET6)
        {
            return false;
        }
#endif
        else
        {
            return false;
        }
    };

} //namesapce bfcp

HashValueImp(bfcp::BfcpNetTuple, data.hash());

/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@vovida.org.
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */
